# -*- coding: utf-8 -*-

"""
@author: Laowang
@contact: QQ:1125564921
@Created on: 2022/6/20
@Remark: 
"""
import json
import redis
from typing import Callable, Union, Any
from contextlib import contextmanager

import sqlalchemy
from sqlalchemy.orm import declarative_base, sessionmaker
from sqlalchemy import create_engine
from framework_strategy.gopy import settings

sqlalchemy__version__ = "sqlalchemy version : {}".format(sqlalchemy.__version__)
print(sqlalchemy__version__.center(20, "*"))


_mysql_uri = "mysql+pymysql://{user}:{password}@{host}:{port}/{db}?charset=utf8".format_map(
    {
        "user": settings.MYSQL_USER,
        "password": settings.MYSQL_PASS,
        "host": settings.MYSQL_HOST,
        "port": settings.MYSQL_PORT,
        "db": settings.MYSQL_DB
    }
)


Base = declarative_base()
_create_engine = create_engine


def create_engine() -> Union[Callable, sessionmaker]:

    engine = _create_engine(_mysql_uri, pool_size=20, max_overflow=0)
    Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
    db_session = Session()
    return engine, db_session


engine, _ = create_engine()


@contextmanager
def open_db_session():

    _, session = create_engine()
    yield session
    try:
        session.commit()
        session.close()
    except:
        session.commit()
    finally:
        pass


# -------------------------------------redis---------------------------------------
redis__version__ = "redis version : {}".format(redis.__version__)
print(redis__version__.center(20, "*"))


class OPRedis:
    """
    redis operator
    """
    POOLNAME = "pool"

    def __init__(self, pool=None) -> None:
        """ """
        if not hasattr(self, self.POOLNAME):
            self.pool = pool or OPRedis.get_redis_conn()
        self._cursor = redis.Redis(connection_pool=self.pool, max_connections=10)

    @classmethod
    def get_redis_conn(cls):
        """ pool """
        return redis.ConnectionPool(
            host=settings.REDIS_HOST,
            password=settings.REDIS_PASSWORD,
            port=settings.REDIS_PORT,
            db=settings.REDIS_DB
        )

    def new(self, *,
            host=settings.REDIS_HOST,
            port: int = settings.REDIS_PORT,
            password: str = settings.REDIS_PASSWORD,
            db: int = settings.REDIS_DB):
        """
        Reconstruct the instance,  
        For example, when DB needs different libraries, 
        it can create instances without instantiating the class multiple times,  
        The instance can be rebuilt using built-in methods 
        """
        host = host if host else self.host
        self.pool = redis.ConnectionPool(
            host=host,
            password=password,
            port=port,
            db=db
        )
        self.__init__()

    @classmethod
    def init(cls, *,
             host=settings.REDIS_HOST,
             port: int = settings.REDIS_PORT,
             password: str = settings.REDIS_PASSWORD,
             db: int = settings.REDIS_DB
             ):
        pool = redis.ConnectionPool(
            host=host,
            password=password,
            port=port,
            db=db
        )
        return cls(pool)

    def __getattr__(self, name):
        """ proxy """
        if name == self.POOLNAME:
            return super().__getattr__(name)
        return getattr(self._cursor, name)


class RedisMixnis(OPRedis):

    def __init__(self, pool=None) -> None:
        super().__init__(pool)

    def byte2dict(self, data):
        if not data:
            data = dict()
        elif isinstance(data, bytes):
            data = json.loads(data)
        elif isinstance(data, list):
            data = [json.loads(d) for d in data]
        else:
            raise NotImplementedError('unknow redis data', data)
        return data

    def operator(self, method, **kwargs):
        data = getattr(self._cursor, method)(**kwargs)
        return data
